home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Magnum One
/
Magnum One (Mid-American Digital) (Disc Manufacturing).iso
/
d12
/
flc2.arc
/
FLEXLIST.DOC
< prev
next >
Wrap
Text File
|
1990-12-07
|
45KB
|
966 lines
FlexList II for ANSI C
Copyright 1990
John W. Small
All rights reserved
PSW / Power SoftWare
P.O. Box 10072
McLean, Virginia 22102 8072
(703) 759-3838
10/4/90
CLAIM TO PROPRIETARY TECHNIQUES
Several features of FlexList used in combination make FlexList
unique and versatile. The hybrid stack-queue-list-array data
structure and the techniques used to implement it as a generic
polymorphic object can be easily ported to other computer
languages and remain intact in their essence. The author claims
all rights to these features.
SHAREWARE NOTICE
This FlexList packet is offered to you as "shareware" meaning try
before you buy. The shareware version allows you to test the
product to see if it's suitable for your purposes. In order to
legally use FlexList for any other purpose you must first license
it from PSW / Power SoftWare by sending in a registration fee of
$79.95. Upon registration you will receive a printed version of
the complete manual (140+ pages) and both ANSI C and K&R source
code.
SHAREWARE MANUAL
The shareware documentation that follows has been taken from the
actual printed FlexList manual. I've include only the most vital
information. Refer to flexlist.h for FlexList function
prototypes. Flexlist.c contains comments that explain the
virtual functions: FNnew, FNwrite, FNread, and FNdestruct.
Introduction
A FlexList is alot more than just a ready-to-run C linked list.
It's a searchable-sortable-implodable generic heterogeneous-
homogeneous hybrid stack-queue-list-array accessible by value,
reference or node.
Anytime your C application requires a stack, queue, or linked
list, simply include flexlist.h in your source code and then
start defining your stack, queue, and/or list variables as type
FlexList. A FlexList can be initialized, by various constructor
functions, to store any type of data and then accessed as a
stack, queue, list, or even an array interchangeably!
With more than 30 FlexList functions to choose from, you can
push, pop, insert, delete, sort, store, recall, or find, to name
but a few. Access your data by value (copy) or by reference
(pointer). Or move nodes directly between FlexLists. Because
FlexList functions adjust automatically to different types of
data, new sets of primitives needn't be derived to accommodate
new data types. Thus your application's coding time and code
size won't continue to grow when you add new types of FlexLists.
A FlexList can even be made to accommodate heterogeneous data via
its virtual function hooks.
With FlexList you can quickly construct lists of lists or other
composite structures. And since FlexLists are initialized at run
time, the data they hold or even their creation can be specified
at run time thus allowing your application new dimensions of
adaptability to user inputs.
Use FlexList to code your next application's linked lists in
minutes instead of days, without the usual debugging and
documenting headaches associated with custom coding various list
types. It's no problem modifying your application in midstream
to incorporate various and differing list types; with FlexList no
linked list code needs to be scrapped or rewritten. The code
space you'll save over cloning list primitives for different data
types may just save you from having to go the overlay/swapping
route.
Chapter 2. Programming with FlexList
This chapter is a combination of a brief tutorial on using the
FlexList tool and a logical survey of the FlexList functions.
You may find it useful to lookup the FlexList functions in the
reference chapter while reading the example programs here. Don't
worry too much about the details for now. Instead you should
finish this chapter with
1) an idea of the points to remember when programming with
FlexList,
2) a conceptual model of FlexList's hybrid stack-queue-list-
array data structure, and
3) an appreciation for the various ways that a FlexList can be
manipulated and accessed, namely: by value, by reference, or
by node.
After programming for a while with FlexList, be sure to reread
this chapter.
Let's first cover several points that you should keep in mind
when using FlexList in any application.
1. Include flexlist.h in any application using FlexList.
#include <flexlist.h>
2. Define your stack, queue, or list as a FlexList.
FlexList intStack;
3. Before using your FlexList you must call a constructor
function to initialize it for the size of the data that you
will be storing in it.
FLfixed(&intStack,sizeof(int));
4. The address of your FlexList must be passed as the first
parameter to FlexList functions. Where data copying is
involved, the address of the data is passed as the second
parameter. Remember, don't pass the data by value.
for (i = 1; i < 11; i++)
FLpushD(&intStack,&i); /* address of i */
5. FlexList functions return pointers or integers. A returned
value of zero indicates failure of the function to carry out
its task.
while (FLpopD(&intStack,&i)) /* loop until false */
printf("\n %d",i);
6. After you are finished with your FlexList you should call a
FlexList destructor function. In our example it isn't
really necessary since we've popped all the nodes anyway.
Let's include it though for completeness.
FLclear(&intStack);
7. Don't forget to link your application with the object module
created by compiling flexlist.c or with the library you
build.
Let's see the whole program.
#include <stdio.h> /* printf() */
#include <flexlist.h>
main() /* Count backwards from ten via a stack. */
{
FlexList intStack;
int i;
(void) FLfixed(&intStack,sizeof(int));
for (i = 1; i < 11; i++)
(void) FLpushD(&intStack,&i);
while (FLpopD(&intStack,&i))
(void) printf("\n %d",i);
(void) FLclear(&intStack);
return 0;
}
Dynamic FlexList Constructors
Let's continue with a survey of FlexList's various functions.
The FlexList tool has several constructor/destructor functions.
Why? Well the FlexList that we just used in the previous example
is a local variable. Actually only the FlexList header is a
local variable - the nodes in a FlexList are almost always
dynamically allocated! Suppose that we want the FlexList
variable itself to be dynamically allocated? Then we would use
the constructor function FLfixedNew.
Let's see that program again.
#include <stdio.h> /* printf() */
#include <flexlist.h>
main() /* Count backwards from ten via a stack. */
{
FlexL intS;
int i;
if ((intS = FLfixedNew(sizeof(int),0,FLDdestruct0))
== FlexL0) return 1;
for (i = 1; i < 11; i++)
(void) FLpushD(intS,&i);
while (FLpopD(intS,&i))
(void) printf("\n %d",i);
(void) FLdelete(&intS);
return 0;
}
Notice that the integer stack is now defined as type FlexL
instead of FlexList. FlexL is a pointer to a FlexList. I
contracted intStack to intS also as a reminder that it is a
pointer to a FlexList and not a FlexList. The constructor
function FLfixedNew returns a pointer to the FlexList it
allocates and initializes, otherwise it returns 0 or more
specifically (FlexL) 0. I've contracted all NULL constants and
defined them in flexlist.h, e.g. (FlexL) 0 is FlexL0. Several
additional parameters are also required by FLfixedNew but don't
worry about that now. Please note that FlexList primitives take
a FlexList pointer as their first parameter, with the exception
of FLdelete. That's why I didn't use the address of operator,
"&", with "intS" in this version like I did with "&intStack" in
the first.
The point of the two previous examples is that FlexList
constructor/destructor functions come in two varieties: those
that construct/destruct FlexList variables defined at compile
time, i.e. local, static, and global variables, versus their
counter parts that construct/destruct FlexList variables
dynamically allocated at run time.
FlexList Constructor/Destructor Functions
1. Constructor/destructor functions for FlexLists defined at
compile time:
FLfixed() FLunpack() FLvariant()
FLstr() FLclear()
2. Constructor/destructor functions for FlexLists allocated at
run time:
FLfixedNew() FLunpackNew() FLvariantNew()
FLstrNew() FLdelete()
The FLunpack and FLunpackNew functions are used to explode C
arrays (vectors) into FlexLists. Each FlexNode holds one cell of
the array (see section on compaction functions). FLvariant and
FLvariantNew construct FlexLists that have variant sized
FlexNodes (see FLvariant in reference chapter to see how to
construct heterogeneous FlexLists)! FLstr and FLstrNew are
actually macros that call FLvariant and FLvariantNew
respectively, constructing FlexLists that contain FlexNodes just
big be enough to hold C strings within. FLclear deallocates all
FlexNodes in a FlexList, while FLdelete calls FLclear and then
proceeds to deallocate the dynamically allocated FlexList
variable (header).
FlexList Header Access Functions
As you become more familiar with what's inside a FlexList you
will want to access the data fields inside the FlexList header.
Never access these fields directly! There is little performance
penalty since most of these functions are in fact macros. You
should decide right now that you are only going to access the
guts of a FlexList via FlexList functions. This insures the
integrity of the FlexList by imposing the OOP (Object Oriented
Programming) concept of encapsulation. The C language doesn't
enforce access restriction as does the C++ language, so you're on
the honor system!
FLfrontD() FLcurrentD() FLrearD()
FLcurNum() FLnodes() FLmaxNodes()
FLsetMaxNodes() FLnotFull() FLsizeofNodeData()
FLisSorted() FLunSort() FLcompare()
FLsetCompare() FLisFixed() FLisVariant()
FLData()
A FlexList can be accessed as a stack, queue, list, or even an
array once it has nodes in it, interchangeably. The FlexList
header keeps track of everything so if your pushing data onto
your "stack" at one moment, you can recall an element from your
"array" the next. You can sort your FlexList, then perform
various other operations on it and FLisSorted will tell you if it
is still sorted. You can set an upper limit on the number of
nodes a FlexList is allowed to hold with FLsetMaxNodes. You will
come to know what each function does with time.
FlexList Stack and Queue Functions
A stack provides LIFO (Last In, First Out) storage. A complete
set of stack primitives is provided. These functions don't
disturb the queue, list, or array structure of a FlexList. In
other words, you can access your FlexList as a stack at any time
and revert back to accessing it as a queue, list, or array
freely.
FLpushN() FLpushD() FLpopN()
FLpopD() FLtopD() FLinsQN()
FLinsQD() FLfrontD() FLrearD()
A queue provides FIFO (First In, First Out) storage. Notice that
some of the stack functions double up here as queue functions,
e.g. FLpopD, FLtopD. Also I have again included some of the
FlexList header access functions, i.e. FLfrontD and FLrearD which
return pointers to the data in the first and last nodes
respectively. Basically I'm suggesting various logical ways to
categorize FlexList functions to help you remember them. You'll
find that several functions may belong in multiple categories.
The functions ending with "N" work directly with the FlexNodes
which contain your data. For an example, suppose that you want
to pop your stack directly into your queue. The following code
shows how.
#include <stdio.h> /* printf() */
#include <flexlist.h>
FlexList s, q;
int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int *iptr;
#define inT0 ((int *)0)
main() /* count to ten the hard way */
{
(void) FLunpack(&s,sizeof(a[0]),sizeof(a)/
sizeof(a[0]),a);
(void) FLfixed(&q,FLsizeofNodeData(&s));
while ((iptr = FLinsQN(&q,FLpopN(&s))) != inT0)
(void) printf("\n %d",*iptr);
(void) FLclear(&s);
(void) FLclear(&q);
return 0;
}
In this example the stack is constructed by "exploding" an array
of integers into a FlexList. Next the queue is constructed to
hold the same size data as the stack. Then the stack is popped
directly into the queue and a pointer to the data in the newly
inserted queue node is captured and tested to control the
looping! I print the integer each time so you can see that it
made it into the queue. Did you notice how I defined the NULL
integer pointer? I did that here so you'll know what's going on
when you see other NULL pointers elsewhere.
FlexList List Functions
Any FlexList can be treated as a doubly linked list thereby
providing sequential storage. You can insert nodes or delete
them. The FlexList header maintains a current node pointer that
lets the list functions know where the insertion or deletion is
to take place. Stack and queue functions don't disturb this
current pointer unless of course it points to the top/front of
the stack/queue and that node is popped/removed. In that case
the current node becomes undefined.
FLmkcur() FLinsN() FLinsD()
FLinsSortN() FLinsSortD() FLdelN()
FLdelD() FLnextD() FLprevD()
FLcurNum() FLcurrentD() FLcompare()
FLsetCompare()
FlexList nodes are numbered starting at one. When FLnextD
reaches the end of the list the current node number is set to
zero which is said to be undefined and FLnextD returns the NULL
void pointer. On the next call to FLnextD the current node
becomes one, that is of course if there are nodes in the
FlexList! FLprevD works the same way. This mechanism makes it
handy to walk around lists pausing at the ends. If you want to
set the current pointer to a particular node, use FLmkcur.
Oh, I guess I should mention again that you can store any type of
data in a FlexList not just integers. Let's see something like
that last example again with strings.
#include <stdio.h> /* printf() */
#include <flexlist.h>
FlexList l;
char *a[] = { "Now is the time", "for all good",
"programmers to cut", "their coding time",
"with FlexList!"
};
char **sptr;
#define chaRR0 ((char **)0)
main() /* PSW propaganda */
{
(void) FLunpack(&l,sizeof(a[0]),sizeof(a)/
sizeof(a[0]),a);
while ((sptr = FLnextD(&l,(void *)0)) != chaRR0)
(void) printf("\n %s",*sptr);
(void) FLmkcur(&l,FLnodes(&l));
while (FLdelD(&l,(void *)0))
/* null statement */; /* FLclear(&l); */
(void) printf("\n There should be zero nodes now: %d",
FLnodes(&l));
return 0;
}
In this program I exploded a vector of char pointers into a
FlexList. Whenever FlexLists are constructed the current node is
set as undefined so FLnextD starts walking across the FlexList
beginning with the first node. FLnextD will copy the data in the
next node to whatever address you specify in the second parameter
unless of course it is the NULL address! When you are browsing
the reference chapter you will notice many FlexList functions
that copy data to/from a FlexNode when an address is given. If
the NULL address is specified the copying action is inhibited but
the function otherwise performs its purpose. In this example
FLnextD is inhibited from copying any data but it never the less
moves the current pointer on to the next node. FLnextD also
returns a void pointer to the data in the next node. I capture
that pointer in order to print the string but I'm also using it
to control the loop.
All FlexList functions return either pointers or integer types
that are non zero only when the respective function completes
successfully. The void pointers returned from FlexList functions
point to the user data in focus. In the case of FLnextD it
points to the user data in the next node. I have simply type
casted this pointer to a pointer to the type of data I'm storing
in the FlexNodes in order to gain access to that data. As you
can see FlexList functions allow you to access you data by value
(copy) or by reference (pointer) as well as moving nodes directly
between/within FlexLists.
Back to our example, FLmkcur moves the current node pointer to
the last node in the list. FLmkcur also returns a pointer to the
data in the last node but I choose to ignore it. Now FLdelD is
used to delete the nodes from the list. FLdelD removes the
current node after copying its contents to the address specified
(copying is inhibited here by the NULL address) and then unlinks
the FlexNode from the FlexList and proceeds to deallocate it
making the previous node in the FlexList the new current node. I
might add that FLinsD does the exact opposite, i.e. inserts a new
FlexNode after the current node making the new node current.
Again the stack and queue structure of FlexList is undisturbed by
any list functions invoked on the FlexList and vice versa.
FlexList Search/Sort Functions
The Search/Sort functions allow you to lookup and/or sort your
data. Every FlexList has a user definable compare function. Use
FLsetCompare to set a FlexList's internal compare function
pointer. To facilitate sorting define your compare function to
return -1, 0, or 1 to indicate whether the first data being
compared is less than, equal to, or greater than the second,
respectively. The compare function is also used for matching in
the FLfind...() functions. Please note that most of the
functions mentioned here modify the current node setting, thus
they interact with the list functions mentioned earlier.
FLinsSortN() FLinsSortD() FLfindFirstD()
FLfindNextD() FLfindLastD() FLfindPrevD()
FLsort() FLisSorted() FLunSort()
FLcompare() FLsetCompare()
Use FLinsSortD/N() to perform insertion sorts. If the list isn't
sorted then FLsort will be called automatically before attempting
the insertion. If a compare function pointer hasn't been setup
then the insertion is aborted.
The FLfind...D functions work regardless if the FlexList is
sorted or not. If it is sorted then the binary search algorithm
is used to find the first/last matching data and a linear search
is used to find the next/previous match stopping immediately
after the first mismatch. If the FlexList is not sorted then the
linear search algorithm is used to find the first/last matching
data and again to find the next/previous match stopping only at
the ends of the list.
Use FLsort or FLsetCompare to setup the compare function pointer.
If the NULL compare function pointer, FLcompare0 (defined in
flexlist.h), is passed to FLsort then the previous compare
function pointer is used to sort the list. Call FLisSorted to
determine whether a FlexList is still sorted from its last sort.
For example, suppose you sort a list and pop some nodes off the
front. Well that won't cause the FlexList to be unsorted so
FLisSorted will return true. But if you push some data onto it
treating it like a stack, that may or may not ruin the sorted
order. FLisSorted will return false because it can no longer
guarantee that the FlexList is sorted. You need to be careful
though since FLnextD, FLprevD, and FLmkcur won't cause FLisSorted
to reset to false even though you might have modified the data in
the FlexNode via the returned void pointer! In that case you may
want to call FLunSort to reset FLisSorted so that it will return
false.
The following example performs an unsorted linear search followed
by an alphabetical sort.
#include <stdio.h> /* printf() */
#include <string.h> /* strstr(), strcmp() */
#include <flexlist.h>
int strStr(char *s, char *t)
{
return !(int)strstr(s,t);
}
main()
{
FlexList l;
char *s, sbuf[80];
char *mask = "overtime";
int i;
(void) FLstr(&l);
(void) FLinsD(&l,"Once upon a time there were three");
(void) FLinsD(&l,"programmers, who worked");
(void) FLinsD(&l,"allot of overtime!");
(void) FLinsQD(&l,"That was before the FlexList Fox");
(void) FLinsQD(&l,"joined the staff!");
for ((void)FLmkcur(&l,0); FLnextD(&l,sbuf);
/* no reinit */)
(void) printf("\n Node: %d. %s",
FLcurNum(&l),sbuf);
(void) FLsetCompare(&l,FLcomparE(strStr));
if ((s = FLfindFirstD(&l,mask)) != (char *)0)
(void) printf("\n Mask: \"%s\" found in \
node: %d. %s",mask,FLcurNum(&l),s);
(void) FLsort(&l,FLcomparE(strcmp));
for (i = 1; FLrecallD(&l,sbuf,(unsigned)i); i++)
(void) printf("\n Node: %d. %s",
FLcurNum(&l),sbuf);
(void) FLclear(&l);
return 0;
}
This program constructs a variant FlexList via the macro
constructor FLstr which expands into a call to FLvariant. The
FlexNodes in this FlexList are only be enough to hold the
character strings passed as parameters (please note that
character string constants are pointers to the strings so I
didn't use the address of, "&", operator!) I didn't use the
FLunpack constructor because it builds fixed size FlexNode
(homogeneous) FlexLists - an array has fixed cell sizes. Instead
I inserted the strings one by one and queued the last two just to
be different.
The first "for loop" uses FLnextD to verify the contains of the
FlexNodes. FLnextD only copies the string and zero terminator
thanks to the virtual function table's function pointers (see
FLvariant in the reference chapter to see how to construct
heterogenous lists). Be sure that the variable whose address you
pass to FLnextD is big enough to accommodate the largest data
that can be read! You should have noticed that I had to use
FLmkcur(&l,0) since the current node pointer is still pointing to
the last node inserted and not the last node queued. Remember,
queue functions don't disturb the list structure - the current
node concept is part of the list structure, not the queue
structure!
Since the list is not sorted, FLfindFirstD searches in a linear
fashion starting with the first node, internally calling the
compare function setup with FLsetCompare. The FLcomparE macro,
defined in flexlist.h, type casts strStr to the precise function
pointer type required by both FLsetCompare and FLsort. My strStr
returns 0 if t is within s. Since only a linear search will be
used with this compare function, it doesn't matter that is
doesn't return -1 or 1 to indicate less than or greater than.
The program proceeds with a standard alphabetical sort and the
resultant ordering is displayed with FLrecallD, an array access
function which you'll see in the next section.
FlexList Array Functions
An array provides randomly accessible storage. A FlexList can
also be accessed as an array once it has nodes in it.
FLstoreD() FLrecallD() FLmkcur()
FLcurrentD() FLcurNum()
The last node accessed becomes the new current node. When
randomly accessing a FlexList, FLmkcur is called internally by
both FLstoreD and FLrecallD to make the requested node current.
FLmkcur determines which pointer (front, current, or rear) is
closest to the requested node. It then follows the FlexList's
linkage from that closest pointer to the newly requested node.
The current pointer is left positioned at the new node. Please
note that array, search/sort, and list functions interact in that
they all work with and modify the current node setting. Stack
and queue functions are independent, however.
FlexList Compaction Functions
Sometimes you want to work with a list, other times it's more
convenient to work with an array. Although FlexList allows this
chameleon behavior, your application may progress in stages that
favor a linked list at one point and a conventional C array at
another. FlexList has "compaction" functions for converting a
conventional array into a FlexList or vice versa, thus allowing
you to optimize the performance of your application.
FLunpack() FLunpackNew()
FLpack() FLpackPtrs()
You can think of the FLunpack/FLunpackNew as exploding the
conventional C array into a FlexList. Think of the FLpack
function as doing the exact opposite or imploding a FlexList into
a conventional C array. Arrays compacted by FLpack and
FLpackPtrs must be explicitly deleted with a call to free when no
longer needed if their memory is ever to be recovered for
subsequent reuse. FLpackPtrs returns an array of void pointers
pointing to the data in the FlexNodes. This array is NULL
terminated to facilitate subsequent processing.
For the last example program we'll clone a vector of char
pointers but the clone will have the advantage over its parent of
being sorted in alphabetical order.
#include <stdio.h> /* printf() */
#include <stdlib.h> /* free() */
#include <string.h> /* strcmp() */
#include <flexlist.h>
char *a[] = { "one", "two", "three", "four", "five" };
char **A;
int strCmp(char **s, char **t)
{
return strcmp(*s,*t);
}
main()
{
FlexList l;
int i;
(void) FLunpack(&l,sizeof(a[0]),sizeof(a)/
sizeof(a[0]),a);
(void) FLsort(&l,FLcomparE(strCmp));
A = FLpack(&l);
for (i = 0; i < sizeof(a)/sizeof(a[0]); i++)
(void) printf("\n a[%d] = %s",i,a[i]);
if (A) {
for (i = 0; i < FLnodes(&l); i++)
(void) printf("\n A[%d] = %s",
i,((char **)A)[i]);
free(A);
}
(void) FLclear(&l);
return 0;
}
Remember that the FlexNodes in this program contain char pointers
and that the compare function is passed pointers to the char
pointers, that is why the strCmp has to dereference them.
Many commercial C toolbox functions require vector parameters.
With FlexList's compaction functions you can manipulate vectors
on the fly. The vectors can be stored in files and loaded as
needed via a FlexList and subsequent packing. The vector can
then be kept around only during the computation phase in which it
is needed. Large, oversized, static vectors need not be
allocated at compile time to cope with worst case scenarios,
thereby consuming precious data segment space within your
program.
Accessing FlexList Nodes and Data
Now that we've finished categorizing FlexList functions along the
lines of its hybrid stack-queue-list-array structure, let's
rethink what we've seen in terms of how the FlexList was
accessed, namely: by value, by reference, and by node. This
gives us a second way to analyze FlexList functions.
"By value" implies that the data is copied to or from a FlexNode.
For example, with FLpopD the top node of the stack is popped and
the data is copied to the address specified by the second
parameter before the node is freed and returned to the heap. We
are in effect accessing the data in the top node by value since
we are retrieving, in actuality, a copy of it.
FLpopD(&s,&i); /* i is same type in FlexNode */
"By reference" implies that the data in the FlexNode is accessed
by pointer. You will need to type cast these void pointers to
pointers to the type of data you have stored in the FlexNodes.
Suppose we're walking backward across a FlexList of integers with
the code:
while ((iptr = FLprevD(&l,(void *)0)) != (int *)0)
(void) printf("\n %d",*iptr);
All void pointers returned by FlexList functions point to user
data. Thus the data can be modified directly. "By reference"
functions are provided to speed up processing. Without pointer
access you would have to first copy the data out of the FlexNode,
modify it and then copy it back. With large data structures that
could prove to be quite inefficient. Please note the FLnextD and
FLprevD are purely "by reference" when called with their second
parameters equal to the NULL address. Actually all FlexList
functions returning void pointers allow access "by reference" in
combination perhaps with "by value" access.
In queuing network simulations you will want to move nodes
between FlexLists rapidly. Without access "by node" you would
have to "FLpopD" the source queue and "FLinsQD" the destination
queue. This would entail copying the data from the front node
into a local variable of the same type, unlinking and
deallocating the FlexNode, then reallocating a new FlexNode and
linking it into the other queue and finally copying the data from
the local variable back into the node. The faster way is to
unlink the FlexNode from the source queue and relink it directly
into the destination queue, e.g.
FLinsN(&dstQ,FLpopN(&srcQ));
If dstQ is already full then the node popped from srcQ is lost in
the twilight zone. A better approach would be to prevent the
chance of FlexNodes being left dangling:
while (FLnotFull(&dstQ) && FLnodes(&srcQ))
(void) FLinsN(&dstQ,FLpopN(&srcQ));
The "By node" functions unlink and relink FlexNodes directly
between compatible FlexLists. Compatible in this sense means
that both FlexLists have equal sizeofNodeData fields (not
necessarily initialized for the same data type) . Use the
FLsizeofNodeData function to read the sizeofNodeData field. Two
FlexLists could also be considered to be compatible if they are
both variant FlexLists with compatible data. I should point out
that variant FlexLists have the sizeofNodeData fields set to
zero. Be careful not to assume that two FlexLists are compatible
just because both sizeofNodeData fields are equal to zero. You
can use FLisVariant to retrieve the virtual function table
pointer within the FlexList header. If two variant FlexLists use
the same virtual function table, they are most likely compatible.
You are responsible for insuring that your FlexLists are
compatible. Remember also, FlexNodes unlinked with a function
ending with "N" must be relinked with a function ending with "N"
to a compatible FlexList or otherwise explicitly disposed of.
Summary
Let's review what we learned starting with the points to remember
when programming with the FlexList tool.
1. Be sure to include the FlexList header in any module
that references FlexList functions.
2. Define your global, static, and local stacks, queues,
lists, etc. as "FlexList". Define your dynamic
versions as pointers to FlexLists, i.e. "FlexL".
3. Construct your FlexList variable with a static
constructor function and your FlexList pointer with a
dynamic constructor function.
4. The first parameter of all FlexList functions, with the
exception of FLdelete, is an address of a FlexList, or
putting it another way, is a pointer to a FlexList. If
the function is somehow involved with the copying of
user data, the address of the data is always passed in
the second parameter.
5. All FlexList functions return either pointers or
integers. A returned value of zero indicates failure
of the function to complete its task.
6. Destruct all FlexLists when they are no longer needed.
Use FLclear on global, static, or local FlexList
variables and FLdelete on dynamic FlexLists.
7. Link your application with the compiled flexlist object
module or the flexlist library you build.
Next, let's review the various functions available in the
FlexList tool. FlexList functions can be categorized according
to the logical nature of a FlexList's inherent hybrid stack-
queue-list-array structure.
FlexList Constructor/Destructor Functions
FLfixed() FLunpack() FLvariant()
FLstr() FLclear()
FLfixedNew() FLunpackNew() FLvariantNew()
FLstrNew() FLdelete()
FlexList Header Access Functions
FLfrontD() FLcurrentD() FLrearD()
FLcurNum() FLnodes() FLmaxNodes()
FLsetMaxNodes() FLnotFull() FLsizeofNodeData()
FLisSorted() FLunSort() FLcompare()
FLsetCompare() FLisFixed() FLisVariant()
FLData()
FlexList Stack and Queue Functions
FLpushN() FLpushD() FLpopN()
FLpopD() FLtopD() FLinsQN()
FLinsQD() FLfrontD() FLrearD()
FlexList List Functions
FLmkcur() FLinsN() FLinsD()
FLinsSortN() FLinsSortD() FLdelN()
FLdelD() FLnextD() FLprevD()
FLcurNum() FLcurrentD() FLcompare()
FLsetCompare()
FlexList Search/Sort Functions
FLinsSortN() FLinsSortD() FLfindFirstD()
FLfindNextD() FLfindLastD() FLfindPrevD()
FLsort() FLisSorted() FLunSort()
FLcompare() FLsetCompare()
FlexList Array Functions
FLstoreD() FLrecallD() FLmkcur()
FLcurrentD() FLcurNum()
FlexList Compaction Functions
FLunpack() FLunpackNew() FLpack()
FLpackPtrs()
Finally, FlexLists can be accessed "by value", "by reference",
and "by node." "By value" entails the copying of user data
between the FlexNodes and user specified addresses. If the
address specified is NULL than the copying of data is inhibited
but the function performs otherwise as expected. The address is
always the second parameter of the FlexList function requiring
it. "By reference" infers that user data is manipulated directly
inside a FlexNode via a pointer. All FlexList functions
returning void pointers allow "by reference" access. "By node"
functions allow FlexNodes to be yanked/inserted from/into
FlexLists. These functions are named with "N" suffixes. Move
FlexNodes only between compatible FlexLists. Don't leave
FlexNodes dangling: either insert them back into a compatible
FlexList with an "N" function or explicitly free them.
By now you should be ready to start the planning and subsequent
coding of your FlexList application with the help of the
reference chapter on FlexList functions. Later, when you feel
comfortable with FlexList, you will want to try your hand at
deriving your own variant FlexLists (lists that contain
heterogeneous data). Be sure to read the entry for FLvariant in
the reference chapter for an explanation of writing your own
FlexList virtual functions that accommodate your heterogeneous
data. The FlexList dynamic constructor entries, i.e. constructor
functions ending with "New", explain how to encapsulate your
list-pertinent data within FlexList headers.